package com.south.base.test.arithmetic.fsm;

import org.junit.Assert;
import org.junit.Test;

/**
 * @author Administrator
 * @date 2019/11/27 10:54
 */
public class String2Number {

    /**
     * 验证给定的字符串是否可以解释为十进制数字。
     */
    @Test
    public void isNumber() {
        Assert.assertTrue(isNumber("0"));
        Assert.assertTrue(isNumber(" 0.1 "));
        Assert.assertTrue(isNumber("2e10"));
        Assert.assertTrue(isNumber(" -90e3   "));
        Assert.assertTrue(isNumber(" 6e-1"));
        Assert.assertTrue(isNumber("53.5e93"));
        Assert.assertFalse(isNumber("abc"));
        Assert.assertFalse(isNumber("1 a"));
        Assert.assertFalse(isNumber(" 1e"));
        Assert.assertFalse(isNumber("e3"));
        Assert.assertFalse(isNumber(" 99e2.5 "));
        Assert.assertFalse(isNumber(" --6 "));
        Assert.assertFalse(isNumber("-+3"));
        Assert.assertFalse(isNumber("95a54e53"));
        Assert.assertTrue(isNumber2("0"));
        Assert.assertTrue(isNumber2(" 0.1 "));
        Assert.assertTrue(isNumber2("2e10"));
        Assert.assertTrue(isNumber2(" -90e3   "));
        Assert.assertTrue(isNumber2(" 6e-1"));
        Assert.assertTrue(isNumber2("53.5e93"));
        Assert.assertFalse(isNumber2("abc"));
        Assert.assertFalse(isNumber2("1 a"));
        Assert.assertFalse(isNumber2(" 1e"));
        Assert.assertFalse(isNumber2("e3"));
        Assert.assertFalse(isNumber2(" 99e2.5 "));
        Assert.assertFalse(isNumber2(" --6 "));
        Assert.assertFalse(isNumber2("-+3"));
        Assert.assertFalse(isNumber2("95a54e53"));
    }

    private boolean isNumber2(String s) {
        if (s == null || s.length() == 0) {
            return false;
        }
        //标记是否遇到相应情况
        boolean numSeen = false;
        boolean dotSeen = false;
        boolean eSeen = false;
        char[] str = s.trim().toCharArray();
        for (int i = 0; i < str.length; i++) {
            switch (str[i]) {
                case '.':
                    //.之前不能出现.或者e
                    if (dotSeen || eSeen) {
                        return false;
                    }
                    dotSeen = true;
                    break;
                case 'e':
                case 'E':
                    //e之前不能出现e，必须出现数
                    if (eSeen || !numSeen) {
                        return false;
                    }
                    eSeen = true;
                    //重置numSeen，排除123e或者123e+的情况,确保e之后也出现数
                    numSeen = false;
                    break;
                case '+':
                case '-':
                    //+-出现在0位置或者e/E的后面第一个位置才是合法的
                    if (i != 0 && str[i - 1] != 'e' && str[i - 1] != 'E') {
                        return false;
                    }
                    break;
                default:
                    if (str[i] >= '0' && str[i] <= '9') {
                        numSeen = true;
                        break;
                    }
                    return false;
            }
        }
        return numSeen;
    }

    private boolean isNumber(String s) {
        int state = 0;
        int finals = 0b101101000;
        int[][] transfer = new int[][]{{0, 1, 6, 2, -1},
                {-1, -1, 6, 2, -1},
                {-1, -1, 3, -1, -1},
                {8, -1, 3, -1, 4},
                {-1, 7, 5, -1, -1},
                {8, -1, 5, -1, -1},
                {8, -1, 6, 3, 4},
                {-1, -1, 5, -1, -1},
                {8, -1, -1, -1, -1}};
        char[] ss = s.toCharArray();
        for (char c : ss) {
            int id = make(c);
            if (id < 0) return false;
            state = transfer[state][id];
            if (state < 0) return false;
        }
        return (finals & (1 << state)) > 0;
    }

    private int make(char c) {
        switch (c) {
            case ' ':
            case ' ':
                return 0;
            case '+':
            case '-':
                return 1;
            case '.':
                return 3;
            case 'e':
                return 4;
            default:
                if (c >= '0' && c <= '9') return 2;
        }
        return -1;
    }
}
